package br3akr.DeadlyDinnerParty.Server.World;

import java.util.HashSet;
import java.util.Set;

import br3akr.DeadlyDinnerParty.Server.Exceptions.DeadlyDinnerPartyException;

public class Exit {
	public enum ExitDirections {
		NORTH("north"), SOUTH("south"), EAST("east"), WEST("west"), UP("up"), DOWN("down"), NORTHWEST("northwest"), SOUTHWEST("southwest"), NORTHEAST("northeast"), SOUTHEAST("southeast"), SECRET("secret");
		
		private final String name;
		
		private ExitDirections(String name) {
			this.name = name;
		}

		public String toString() {
			return name;
		}
	}
	
	public enum ExitStatus {
		OPEN("open"), CLOSED("closed"), LOCKED("locked"), BASHED ("bashed open"); 
		
		private final String name;
		
		private ExitStatus(String name) {
			this.name = name;
		}
		
		public String toString() {
			return name;
		}
	}
	
	private Area area1;
	private Area area2;
	
	private Exit.ExitDirections area1ExitDir;
	private Exit.ExitDirections area2ExitDir;
	
	private String area1ExitDesc;
	private String area2ExitDesc;
	
	private String area1ExitOnlookerDesc;
	private String area2ExitOnlookerDesc;
	
	private boolean isHidden;
	
	private Set<GameCharacter> knowsSecretExit;
	
	private Exit.ExitStatus exitStatus;

	public Exit(Area area1, Exit.ExitDirections exit1Dir, Area area2, Exit.ExitDirections exit2Dir) {
		this.area1 = area1;
		this.area1ExitDir = exit1Dir;
		this.area2 = area2;
		this.area2ExitDir = exit2Dir;
		this.isHidden = false;
		knowsSecretExit = new HashSet<GameCharacter>();
	}
	
	public Exit(Area area1, Exit.ExitDirections exit1Dir, Area area2, Exit.ExitDirections exit2Dir, boolean isHidden) {
		this.area1 = area1;
		this.area1ExitDir = exit1Dir;
		this.area2 = area2;
		this.area2ExitDir = exit2Dir;
		this.isHidden = isHidden;
		knowsSecretExit = new HashSet<GameCharacter>();
	}
	
	public Exit(Area area1, Exit.ExitDirections exit1Dir, String area1ExitDesc, String area1ExitOnlookerDesc, 
			    Area area2, Exit.ExitDirections exit2Dir, String area2ExitDesc, String area2ExitOnlookerDesc, 
			    boolean isHidden) {
		this.area1 = area1;
		this.area1ExitDir = exit1Dir;
		this.area2 = area2;
		this.area2ExitDir = exit2Dir;
		this.isHidden = isHidden;
		this.area1ExitDesc = area1ExitDesc;
		this.area2ExitDesc = area2ExitDesc;
		this.area1ExitOnlookerDesc = area1ExitOnlookerDesc;;
		this.area2ExitOnlookerDesc = area2ExitOnlookerDesc;
		this.knowsSecretExit = new HashSet<GameCharacter>();
	}
	
	public Exit.ExitStatus getStatus() {
		return exitStatus;
	}
	
	public void setStatus(Exit.ExitStatus status) {
		exitStatus = status;
	}
	
	/**
	 * Returns the direction of this exit in the supplied room.
	 * @param area The area that may or may not contain this exit
	 * @return Exit.ExitDirections of this Exit within the supplied room or null of this exit does not exist within the supplied Area.
	 */
	public Exit.ExitDirections getExitDirection(Area area) {
		Exit.ExitDirections retval = null;
		
		if (area1.equals(area)) {
			retval = area1ExitDir;
		}
		
		if (area2.equals(area)) {
			retval = area2ExitDir;
		}
		
		return retval;
	}
	
	/**
	 * Returns the description of this exit in the supplied room.
	 * @param Area that may or may not contain this exit
	 * @return String description of this Exit within the supplied room or null of this exit does not exist within the supplied Area.
	 */
	public String getExitDescription(Area area) {
		String retval = null;
		
		if (area1.equals(area)) {
			retval = area1ExitDesc;
		}
		
		if (area2.equals(area)) {
			retval = area2ExitDesc;
		}
		
		return retval;
	}
	
	/**
	 * Returns the description of this exit for onlookers in the supplied room.
	 * @param Area that may or may not contain this exit
	 * @return String description of this Exit for onlookers within the supplied room or null of this exit does not exist within the supplied Area.
	 */
	public String getExitOnlookerDescription(Area area) {
		String retval = null;
		
		if (area1.equals(area)) {
			retval = area1ExitOnlookerDesc;
		}
		
		if (area2.equals(area)) {
			retval = area2ExitOnlookerDesc;
		}
		
		return retval;
	}
	
	/**
	 * Adds to the list of characters who know about this exit.
	 * @param GameCharacter who will be added to the list.
	 */
	public void addKnowsSecretExit(GameCharacter gc) {
		this.knowsSecretExit.add(gc);
	}
	
	/**
	 * Returns true if the supplied GameCharacter knows about this secret exit
	 * @param GameCharacter to test against
	 * @return true if the supplied GameCharacter knows about this secret exit, otherwise returns false
	 */
	public boolean knowsSecretExit(GameCharacter gc) {
		for (GameCharacter ch : knowsSecretExit) {
			if (ch.equals(gc)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Returns true if this is a hidden exit.
	 * @return true if the exit is hidden, otherwise false.
	 */
	public boolean isHidden() {
		return isHidden;
	}
	
	public Area getExitArea(Area fromArea) {
		Area retval = null;
		
		if (area1.equals(fromArea)) {
			retval = area2;
		}
		
		if (area2.equals(fromArea)) {
			retval = area1;
		}
		
		return retval;
	}
	
	public static Exit.ExitDirections getOppositeDirection(Exit.ExitDirections exit) {
		 Exit.ExitDirections retval = null;
		switch (exit) {
		case NORTH:
			retval = ExitDirections.SOUTH;
			break;
		case SOUTH:
			retval = ExitDirections.NORTH;
			break;
		case EAST:
			retval = ExitDirections.WEST;
			break;
		case WEST:
			retval = ExitDirections.EAST;
			break;
		case NORTHWEST:
			retval = ExitDirections.SOUTHEAST;
			break;
		case NORTHEAST:
			retval = ExitDirections.SOUTHWEST;
			break;
		case SOUTHWEST:
			retval = ExitDirections.NORTHEAST;
			break;
		case SOUTHEAST:
			retval = ExitDirections.SOUTHWEST;
			break;
		case UP:
			retval = ExitDirections.DOWN;
			break;
		case DOWN:
			retval = ExitDirections.UP;
			break;
		}
		
		return retval;
	}
	
	public static ExitDirections getDirection(String exit) throws DeadlyDinnerPartyException {
		switch (exit.toLowerCase()) {
			case "west":
			case "w":
				return ExitDirections.WEST;
			case "north":
			case "n":
				return ExitDirections.NORTH;
			case "south":
			case "s":
				return ExitDirections.SOUTH;
			case "east":
			case "e":
				return ExitDirections.EAST;
			case "northwest":
			case "nw":
				return ExitDirections.NORTHWEST;
			case "southwest":
			case "sw":
				return ExitDirections.SOUTHWEST;
			case "northeast":
			case "ne":
				return ExitDirections.NORTHEAST;
			case "southeast":
			case "se":
				return ExitDirections.SOUTHEAST;
			case "down":
				return ExitDirections.DOWN;
			case "up":
				return ExitDirections.UP;
			case "secret":
				return ExitDirections.SECRET;
			default:
				return null;
		}
	}
}